﻿using UnityEngine;
using System.Collections;
using UniLua;

namespace TCG.FFI
{
    /// <summary>
    /// 将UnityEngine.Vector3导出为Lua可用的模块。Vector3是结构体，因此在调用会修改其内部状态的方法后，需要在
	/// lua中重新对变量赋值。
	/// 
	/// 未实现：
	/// * Normalize () : 其使用和normalized相同
	/// * OrthoNormalize () : 暂无需求
	/// * operator != : metamethod没有相应的方法 
	/// 
	/// 注意：
	/// * SmoothDamp : 在lua中调用时会返回两个值，第1个是函数调用的结果，第2个是被修改的currentVolecity参数
    /// </summary>
    public static class UnityVector3Lib
    {
		#region stuff for exporting to lua
		public const string LIB_NAME = "UnityVector3Lib";
		#endregion
		
		#region static variables

		public static int lf_back (ILuaState luaState)
		{
			LuaTools.L_PushUserData (luaState, Vector3.back, LIB_NAME);
			return 1;
		}

		public static int lf_down (ILuaState luaState)
		{
			LuaTools.L_PushUserData (luaState, Vector3.down, LIB_NAME);
			return 1;
		}

		public static int lf_forward (ILuaState luaState)
		{
			LuaTools.L_PushUserData (luaState, Vector3.forward, LIB_NAME);
			return 1;
		}

		public static int lf_left (ILuaState luaState)
		{
			LuaTools.L_PushUserData (luaState, Vector3.left, LIB_NAME);
			return 1;
		}

		public static int lf_one (ILuaState luaState)
		{
			LuaTools.L_PushUserData (luaState, Vector3.one, LIB_NAME);
			return 1;
		}

		public static int lf_right (ILuaState luaState)
		{
			LuaTools.L_PushUserData (luaState, Vector3.right, LIB_NAME);
			return 1;
		}

		public static int lf_up (ILuaState luaState)
		{
			LuaTools.L_PushUserData (luaState, Vector3.up, LIB_NAME);
			return 1;
		}

		public static int lf_zero (ILuaState luaState)
		{
			LuaTools.L_PushUserData (luaState, Vector3.zero, LIB_NAME);
			return 1;
		}

		#endregion

		#region variables

		public static int mf_Magnitude (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			luaState.PushNumber (vector.magnitude);
			return 1;
		}

		public static int mf_Normalized (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			LuaTools.L_PushUserData (luaState, vector.normalized, LIB_NAME);
			return 1;
		}

		public static int mf_SqrtMagnitude (ILuaState  luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			luaState.PushNumber (vector.sqrMagnitude);
			return 1;
		}

		public static int mf_GetX (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData(luaState, 1, LIB_NAME);
			luaState.PushNumber (vector.x);
			return 1;
		}

		public static int mf_SetX (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData(luaState, 1, LIB_NAME);
			double value = luaState.L_CheckNumber (2);
			vector.x = (float)value;
			LuaTools.L_PushUserData (luaState, vector, LIB_NAME);
			return 1;
		}

		public static int mf_GetY (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData(luaState, 1, LIB_NAME);
			luaState.PushNumber (vector.y);
			return 1;
		}

		public static int mf_SetY (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData(luaState, 1, LIB_NAME);
			double value = luaState.L_CheckNumber (2);
			vector.y = (float)value;
			LuaTools.L_PushUserData (luaState, vector, LIB_NAME);
			return 1;
		}

		public static int mf_GetZ (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData(luaState, 1, LIB_NAME);
			luaState.PushNumber (vector.z);
			return 1;
		}

		public static int mf_SetZ (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData(luaState, 1, LIB_NAME);
			double value = luaState.L_CheckNumber (2);
			vector.z = (float)value;
			LuaTools.L_PushUserData (luaState, vector, LIB_NAME);
			return 1;
		}

		#endregion

		#region constructors
		public static int lf_NewVector3 (ILuaState luaState)
		{
			float x = (float)luaState.L_CheckNumber (1);
			float y = (float)luaState.L_CheckNumber (2);
			float z = (float)luaState.L_CheckNumber (3);
			LuaTools.L_PushUserData (luaState, new Vector3 (x, y, z), LIB_NAME);
			return 1;
		}

		#endregion

		#region functions

		public static int mf_Set (ILuaState luaState)
		{
			// Vector3 vector = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			float x = (float)luaState.L_CheckNumber (2);
			float y = (float)luaState.L_CheckNumber (3);
			float z = (float)luaState.L_CheckNumber (4);
			LuaTools.L_PushUserData (luaState, new Vector3 (x, y, z), LIB_NAME);
			return 1;
		}

		public static int mf___string (ILuaState luaState)
		{
			Vector3 vector = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			luaState.PushString (string.Format ("vector3: ({0}, {1}, {2})", vector.x, vector.y, vector.z));
			return 1;
		}

		#endregion

		#region static functions

		public static int lf_Angle (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			luaState.PushNumber (Vector3.Angle (v_1, v_2));
			return 1;
		}

		public static int lf_ClampMagnitude (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			float maxLength = (float)luaState.L_CheckNumber (2);
			LuaTools.L_PushUserData (luaState, Vector3.ClampMagnitude (v_1, maxLength), LIB_NAME);
			return 1;
		}

		public static int lf_Cross (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			LuaTools.L_PushUserData (luaState, Vector3.Cross (v_1, v_2), LIB_NAME);
			return 1;
		}

		public static int lf_Distance (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			luaState.PushNumber (Vector3.Distance (v_1, v_2));
			return 1;
		}

		public static int lf_Dot (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			luaState.PushNumber (Vector3.Dot (v_1, v_2));
			return 1;
		}

		public static int lf_Lerp (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			float t = (float)luaState.L_CheckNumber (3);
			LuaTools.L_PushUserData (luaState, Vector3.Lerp (v_1, v_2, t), LIB_NAME);
			return 1;
		}

		public static int lf_Max (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			LuaTools.L_PushUserData (luaState, Vector3.Max (v_1, v_2), LIB_NAME);
			return 1;
		}

		public static int lf_Min (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			LuaTools.L_PushUserData (luaState, Vector3.Min (v_1, v_2), LIB_NAME);
			return 1;
		}

		public static int lf_MoveTowards (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			float max_delta = (float)luaState.L_CheckNumber (3);
			LuaTools.L_PushUserData (luaState, Vector3.MoveTowards (v_1, v_2, max_delta), LIB_NAME);
			return 1;
		}

		public static int lf_Project (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			LuaTools.L_PushUserData (luaState, Vector3.Project (v_1, v_2), LIB_NAME);
			return 1;
		}

		public static int lf_Reflect (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			LuaTools.L_PushUserData (luaState, Vector3.Reflect (v_1, v_2), LIB_NAME);
			return 1;
		}

		public static int lf_RotateTowards (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			float max_radians_delta = (float)luaState.L_CheckNumber (3);
			float max_magnitude_delta = (float)luaState.L_CheckNumber (4);
			LuaTools.L_PushUserData (luaState, Vector3.RotateTowards (v_1, v_2, max_radians_delta, max_magnitude_delta), LIB_NAME);
			return 1;
		}

		public static int lf_Scale (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			LuaTools.L_PushUserData (luaState, Vector3.Scale (v_1, v_2), LIB_NAME);
			return 1;
		}

		public static int lf_Slerp (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			float t = (float)luaState.L_CheckNumber (3);
			LuaTools.L_PushUserData (luaState, Vector3.Slerp (v_1, v_2, t), LIB_NAME);
			return 1;
		}

		public static int lf_SmoothDamp (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			Vector3 v_3 = (Vector3)LuaTools.L_CheckUData (luaState, 3, LIB_NAME);
			float smooth_time = (float)luaState.L_CheckNumber (4);
			float max_speed = (float)luaState.L_CheckNumber (5);
			float delta_time = (float)luaState.L_CheckNumber (6);
			LuaTools.L_PushUserData (luaState, Vector3.SmoothDamp (v_1,v_2,ref v_3, smooth_time, max_speed, delta_time), LIB_NAME);
			LuaTools.L_PushUserData (luaState, v_3, LIB_NAME);
			return 2;
		}

		#endregion

		#region operators

		public static int mf___sub (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			LuaTools.L_PushUserData (luaState, v_1 - v_2, LIB_NAME);
			return 1;
		}

		public static int mf___mul (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			float d = (float)luaState.L_CheckNumber (2);
			LuaTools.L_PushUserData (luaState, v_1 * d, LIB_NAME);
			return 1;
		}

		public static int mf___div (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			float d = (float)luaState.L_CheckNumber (2);
			LuaTools.L_PushUserData (luaState, v_1 / d, LIB_NAME);
			return 1;
		}

		public static int mf___add (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			LuaTools.L_PushUserData (luaState, v_1 + v_2, LIB_NAME);
			return 1;
		}

		public static int mf___eq (ILuaState luaState)
		{
			Vector3 v_1 = (Vector3)LuaTools.L_CheckUData (luaState, 1, LIB_NAME);
			Vector3 v_2 = (Vector3)LuaTools.L_CheckUData (luaState, 2, LIB_NAME);
			luaState.PushBoolean (v_1 == v_2);
			return 1;
		}

		#endregion

    }
}
